
package ija.model.figures;


import ija.model.basis.Figure;
import ija.model.basis.Position;
import ija.model.types.FigureColor;
import ija.model.types.FigureRoute;
import ija.model.types.Tree;
import ija.xls.notation.DeskCoordinate;
import java.util.LinkedList;
import java.util.List;

/**
 * Třída reprezentuje dámu.
 * 
 * @author Radek Necas, xnecas18
 */
public class Queen extends CheckersFigure{
   
    private final int[] DELTA_VALUES = {-1, 1};
    
    /**
     * Konstruktor vytváří dámu.
     * Vytváří všechna potřebná propojení (pozici bude přidána figurka).
     * 
     * @param p pozice na které bude dáma vytvořena.
     * @param c barva dámy.
     */
    public Queen(Position p, FigureColor c) {
        super(p, c);
    }
    
    public Queen(Position p, Queen copyQueen) {
        super(p, copyQueen);
    }
    
    
    @Override
    public boolean inDirection(Position p) {
        return this.position.sameDiagonal(p);
    }
    
    /**
     * Test zda je povolen pohyb na danou pozici.
     * Dáma se může pohybovat po diagonále libovolným směrem o libovolný počet pozic.
     * 
     * @param p cílová pozice pohybu. Nemusí být součástí téže šachovnice.
     * @return při splnění testu true, jinak false.
     */
    @Override
    public boolean canMove(Position p) {
        if(p.getFigure() != null || !position.sameDiagonal(p))
            return false;
        
        int rowIncrement = getRowIncrement(p);
        int columnIncrement = getColumnIncrement(p);
        int dC = 0, dR = 0;
        Position actPosition;
        do {
            dC += columnIncrement;
            dR += rowIncrement;
            actPosition = position.nextPosition(dC, dR);
            if(actPosition.getFigure() != null)
                return false;
        } while(!actPosition.equals(p));
        
        return true;
    }

    /**
     * Test zda může být proveden skok na danou pozici.
     * Skákat se může pouze o libovolný počet pozic po diagonále.
     * Mezi aktuální pozicí a přeskakovaným pěšce a přeskakovaným pěšcem a cílovou pozicí může být libovolný počet míst.
     * Mezi aktuální a cílovou pozicí musí být pěšec soupeře.
     * Test splní pouze jednoduchý skok. Víceskoky se musí reprezentovat opakovaným voláním.
     * 
     * @param endPosition cílová pozice skoku.
     * @return pozici figurky, jenž je přeskočena (po skoku by měla být odstraněna). Při neúspěchu null.
     */
    @Override
    public Position canJump(Position endPosition) {
        if(!position.sameDiagonal(endPosition) || endPosition.getFigure() != null)
            return null;
        
        int rowIncrement = getRowIncrement(endPosition);
        int columnIncrement = getColumnIncrement(endPosition);
        int dC = 0, dR = 0;
        Position jumpedPosition;
        
        do {        // Hledam v diagonale od soucasne pozice do cilove pozice skoku figurku
            dC += columnIncrement;
            dR += rowIncrement;
            jumpedPosition = position.nextPosition(dC, dR);
            // Pokud dojdu do cile skoku a figurku jsem nenalezl nelze skocit
            if(jumpedPosition.equals(endPosition))
                return null;
        } while(jumpedPosition.getFigure() == null);
        
        // Pokud ma figurka, kterou chci preskocit stejnou barvu jako moje nelze skakat
        if(jumpedPosition.getFigure().getColor() == color)
            return null;
        
        Position stopPosition;
        do {    // Od nalezene figurky do ciloveho pole skoku musi byt prazdno
            dC += columnIncrement;
            dR += rowIncrement;
            stopPosition = position.nextPosition(dC, dR);
            if(stopPosition.getFigure() != null)
                return null;
        } while(!stopPosition.equals(endPosition));
        
        return jumpedPosition;
    }
    
    
    /**
     * Metoda přida všechny volné pohyby od startovací pozice v daném směru.
     * Volné pohyby jsou od startovací pozice do první nalezené figurky nebo konce šachovnice v daném směru.
     * 
     * @param root kořen stromu do kterého se budou pozice přidávat.
     * @param startPosition pozice od které se budou hledat volné pohyby.
     * @param dC rozdíl ve sloupcích [-1, 1] společně s dR udává směr pohybu.
     * @param dR rozdíl v řádcíh [-1, 1] společně s dC udává směr pohybu.
     * @param separator pro retezcovou reprezentaci
     * @return pokud bude přidána alespoň jedna pozice tak true, jinak false.
     */
    public boolean addBlanks(Tree.Node<FigureRoute> root, Position startPosition, int dC, int dR, String separator) {
        if((root == null) || (startPosition == null)) {
            return false;
        }
        
        Position p;
        int colIncr = dC;
        int rowIncr = dR;
        
        while(((p = startPosition.nextPosition(colIncr, rowIncr)) != null) && (p.getFigure() == null)) {
            root.addChild(new FigureRoute(new DeskCoordinate(p), root.getData().getText() + separator + p.toString()));
            colIncr += dC;
            rowIncr += dR;
        }
        
        return(colIncr != dC);
    }
    
    /**
     * Metoda přida všechny volné pohyby od aktuální pozice v daném směru.
     * Volné pohyby jsou od startovací pozice do první nalezené figurky nebo konce šachovnice v daném směru.
     * 
     * @param root kořen stromu do kterého se budou pozice přidávat.
     * @param dC rozdíl ve sloupcích [-1, 1] společně s dR udává směr pohybu.
     * @param dR rozdíl v řádcíh [-1, 1] společně s dC udává směr pohybu.
     * @return pokud bude přidána alespoň jedna pozice tak true, jinak false.
     * @param separator pro retezcovou reprezentaci
     */
    public boolean addBlanks(Tree.Node<FigureRoute> root, int dC, int dR, String separator) {
        return addBlanks(root, this.position, dC, dR, separator);
    }
    
    /**
     * Metoda vrací seznam všechny volných pohybů od startovací pozice v daném směru.
     * Volné pohyby jsou od startovací pozice do první nalezené figurky nebo konce šachovnice v daném směru.
     * 
     * @param startPosition pozice od které se budou hledat volné pohyby.
     * @param dC rozdíl ve sloupcích [-1, 1] společně s dR udává směr pohybu.
     * @param dR rozdíl v řádcíh [-1, 1] společně s dC udává směr pohybu.
     * @return seznam všech pozic.
     */
    public List<Position> getBlanks(Position startPosition, int dC, int dR) {
        List<Position> retList = new LinkedList<>();
        
        Position p;
        int colIncr = dC;
        int rowIncr = dR;
        
        while((p = startPosition.nextPosition(colIncr, rowIncr)) != null && p.getFigure() == null) {
            retList.add(p);
            colIncr += dC;
            rowIncr += dR;
        }
        
        return retList;
    }
    
    
    /**
     * Metoda ukládá pozice všech možných běžných pohybů dámy.
     * Rekurzivní implementace.
     * 
     * @param parrent uzel stromu, do kterého se budou pozice ukládat
     * @return true pokud je nalezen alespoň jeden pohyb, jinak false.
     */
    @Override
    public boolean getMoves(Tree.Node<FigureRoute> parrent) {      
        int[] values = {-1, 1};
        boolean isAdd = false;
        int startSize = parrent.getChildren().size();
        for(int i: DELTA_VALUES) {
            for(int j: DELTA_VALUES) {
                addBlanks(parrent, i, j, "-");
            }
        }
        return (startSize != parrent.getChildren().size());
    }
    
    /**
     * Metoda přidá pozice skoku v daném směru.
     * Jsou přidány všechny volné pozice za přeskakující figurkou.
     * 
     * 
     * @param parrent uzel stromu, do kterého se budou ukládat všechny možné pozice doskoku
     * @return figurka, která by byla přeskočena.
     */
    public Figure addJump(Tree.Node<FigureRoute> root, int dC, int dR) {
    	FigureColor oponentColor = (color == FigureColor.BLACK ? FigureColor.WHITE : FigureColor.BLACK);
        Figure jumpedFigure = findFigureInMovement(dC, dR, oponentColor);
        
        if((jumpedFigure != null) && addBlanks(root, jumpedFigure.getPosition(), dC, dR, "x")) {
            return jumpedFigure;
        }
        
        return null;
    }
    
    
    /**
     * Metoda ukládá všechny pozice při všech možných skocích dámy.
     * Rekurzivní implementace.
     * 
     * @param parrent uzel stromu, do kterého se budou ukládat pozice všech možných skoků
     * @return true pokud je nalezen alespoň jeden pohyb, jinak false.
     */
    @Override
    public boolean getJumps(Tree.Node<FigureRoute> parrent) {
        boolean isAdd = false;
        int startSize = parrent.getChildren().size();
        Figure jumpedFigure;
        Position startPos;
        Position jumpedPos;
        List<Position> freePos;
        
        for(int i: DELTA_VALUES) {
            for(int j: DELTA_VALUES) {
                jumpedFigure = addJump(parrent, j, i);
                if(jumpedFigure != null) {
                    freePos = getBlanks(jumpedFigure.getPosition(), j, i);
                    for(Position p: freePos) {
                        //uloz stav
                        startPos = this.position;
                        jumpedPos = jumpedFigure.getPosition();
                        //skoc
                        //jump(p);
                        uncheckedMove(p);
                        jumpedPos.removeFigure();
                        //rekurze
                        //System.err.println("Queen barvy " + this.color.toString() + " getJumps rekurze z " + startPos.toString() +  " na " + p.toString());
                        getJumps(parrent.getChild(new FigureRoute(new DeskCoordinate(p), null)));
                        //vrat stav
                        this.uncheckedMove(startPos);
                        jumpedPos.putFigure(jumpedFigure);
                    }
                }
            }
        }
        
        return(startSize != parrent.getChildren().size());
    }
    
    
    ////////////////////////////////////////////////////////////////
    // -----------  Soukrome metody ------------------------------//
    ////////////////////////////////////////////////////////////////
    
    
    private int getRowIncrement(Position endPosition) {
        if(endPosition.getRowNumber() - position.getRowNumber() > 0)
            return 1;
        else
            return -1;
    }
    
    private int getColumnIncrement(Position endPosition) {
        if(endPosition.getColumnChar() - position.getColumnChar() > 0)
            return 1;
        else
            return -1;
    }
    
    private Figure findFigureInMovement(int dC, int dR, FigureColor c) {
        Position p = null;
        int colIncr = dC;
        int rowIncr = dR;
        
        while(((p = this.position.nextPosition(colIncr, rowIncr)) != null) && (p.getFigure() == null)) {
            colIncr += dC;
            rowIncr += dR;
        }
        
        Figure f;
        if(p != null) {
            f = p.getFigure();
            if(c == f.getColor())
                return f;   
        }
        
        return null;
    }
    
    private Figure findFigureInMovement(int dC, int dR) {
        return findFigureInMovement(dC, dR, FigureColor.UNKNOWN);
    }

}
